package com.example.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.core.constants.CacheConstants;
import com.example.common.core.constants.Constants;
import com.example.common.core.constants.HttpConstants;
import com.example.common.core.domain.LoginUser;
import com.example.common.core.domain.R;
import com.example.common.core.domain.vo.LoginUserVO;
import com.example.common.core.enums.ResultCode;
import com.example.common.core.enums.StudentStatusEnum;
import com.example.common.core.enums.UserIdentity;
import com.example.common.core.utils.ThreadLocalUtil;
import com.example.common.email.EmailServiceImpl;
import com.example.common.redis.service.RedisService;
import com.example.common.security.exception.ServiceException;
import com.example.common.security.service.TokenService;
import com.example.user.domain.student.Student;
import com.example.user.domain.student.dto.StudentDTO;
import com.example.user.domain.student.dto.StudentSaveDTO;
import com.example.user.domain.student.vo.StudentDetailVO;
import com.example.user.manger.StudentCacheManager;
import com.example.user.mapper.StudentMapper;
import com.example.user.service.IStudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import static com.example.common.core.constants.Constants.USER_ID;
import static com.example.common.core.constants.Constants.USER_KEY;

@Service
@Transactional(readOnly = true)
public class StudentServiceImpl implements IStudentService {

    @Autowired
    private StudentMapper studentMapper;


    @Autowired
    private EmailServiceImpl emailServiceImpl;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private StudentCacheManager studentCacheManager;

    @Autowired
    private RedisService redisService;

    @Value("${sms.code-expiration:5}")
    private long smsCodeExpiration;

    @Value("${sms.send-limit:50}")
    private Integer sendLimit;

    @Value("${jwt.secret}")
    private String secret;

    @Value("${file.oss.downloadUrl}")
    private String downloadUrl;

    private String defaultAvatar = "head_image.png";
    @Override
    public boolean sendCode(StudentDTO studentDTO) {
        String emailCodeKey = getEmailCodeKey(studentDTO.getEmail());
        Long expire = redisService.getExpire(emailCodeKey, TimeUnit.SECONDS);
        if (expire != null && (smsCodeExpiration * 60 - expire) < 60 ){
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }
        //每天的验证码获取次数有一个限制  50次  第二天  计数清0 重新开始计数     计数  怎么存  存在哪
        //操作这个次数数据频繁   、 不需要存储、  记录的次数 有有效时间的（当天有效） redis  String  key：c:t:手机号
        //获取已经请求的次数  和50 进行比较     如果大于限制抛出异常。如果不大于限制，正常执行后续逻辑，并且将获取计数 + 1
        String codeTimeKey = getCodeTimeKey(studentDTO.getEmail());
        Long sendTimes = redisService.getCacheObject(codeTimeKey, Long.class);
        if (sendTimes != null && sendTimes >= sendLimit) {
            throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);
        }
        String code = RandomUtil.randomNumbers(6);
        //存储到redis  数据结构：String  key：p:c:邮箱  value :code
        redisService.setCacheObject(emailCodeKey, code, smsCodeExpiration, TimeUnit.MINUTES);
        // 曲线救国，使用邮箱验证码登录
        boolean b = emailServiceImpl.sendVerificationEmail(studentDTO.getEmail(), "邮件验证码", code, smsCodeExpiration);
        if (!b) {
            throw new ServiceException(ResultCode.FAILED_SEND_CODE);
        }
        redisService.increment(codeTimeKey);
        if (sendTimes == null) {  //说明是当天第一次发起获取验证码的请求
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(codeTimeKey, seconds, TimeUnit.SECONDS);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String codeLogin(String email, String code) {
        checkCode(email, code);
        Student student = studentMapper.selectOne(new LambdaQueryWrapper<Student>().eq(Student::getEmail, email));
        if(student == null){
            // 新用户，新进行注册
            student = new Student();
            student.setEmail(email);
            student.setStatus(StudentStatusEnum.NORMAL.getCode());
            student.setNickName(generateUsername());
            studentMapper.insert(student);
            student = studentMapper.selectOne(new LambdaQueryWrapper<Student>().eq(Student::getEmail, email));
        }
        // 老用户或者用户注册完毕
        // 验证成功，删除redis中的验证码，生成token
        String token = tokenService.createToken(student.getStudentId(), secret, UserIdentity.STUDENT.getValue(), student.getNickName(),student.getHeadImage());
        return token;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean logout(String token) {
        if(!StrUtil.isEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token,secret);
    }

    @Override
    public R<LoginUserVO> info(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if (loginUser == null) {
            return R.fail();
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        if(StrUtil.isEmpty(loginUser.getHeadImage())){
            loginUser.setHeadImage(defaultAvatar);
        }
        loginUserVO.setHeadImage(downloadUrl + loginUser.getHeadImage());
        return R.ok(loginUserVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int edit(StudentSaveDTO studentSaveDTO) {
        Student student = studentMapper.selectById(studentSaveDTO.getStudentId());
        if(student == null){
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        student.setNickName(studentSaveDTO.getNickName());
        student.setEmail(studentSaveDTO.getEmail());
        student.setPhone(studentSaveDTO.getPhone());
        student.setAge(studentSaveDTO.getAge());
        student.setSex(studentSaveDTO.getSex());
        student.setWechat(studentSaveDTO.getWechat());
        student.setSchoolName(studentSaveDTO.getSchoolName());
        student.setMajorName(studentSaveDTO.getMajorName());
        student.setIntroduce(studentSaveDTO.getIntroduce());
        // 刷新redis缓存
        studentCacheManager.refreshStudent(student);
        // 更新loginUser
        tokenService.refreshLoginUser(student.getNickName(),student.getHeadImage(),ThreadLocalUtil.get(USER_KEY,String.class));

        return studentMapper.updateById(student);
    }

    @Override
    public R<StudentDetailVO> detail(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        StudentDetailVO studentDetailVO = studentCacheManager.getStudentById(userId);
        if (studentDetailVO == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if (loginUser == null) {
            return R.fail();
        }
        if(StrUtil.isEmpty(loginUser.getHeadImage())){
            loginUser.setHeadImage(defaultAvatar);
        }
        studentDetailVO.setHeadImage(downloadUrl + loginUser.getHeadImage());
        return R.ok(studentDetailVO);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateHeadImage(String headImage) {
        Long studentId = ThreadLocalUtil.get(USER_ID, Long.class);
        if(studentId == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        Student student = studentMapper.selectById(studentId);
        student.setHeadImage(headImage);
        // redis上会存储用户的登录信息（昵称，头像），所以更新头像后需要刷新缓存
        studentCacheManager.refreshStudent(student);
        tokenService.refreshLoginUser(student.getNickName(),student.getHeadImage(), 
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        return studentMapper.updateById(student);
    }

    private void checkCode(String email, String code) {
        String emailCodeKey = getEmailCodeKey(email);
        String cacheCode = redisService.getCacheObject(emailCodeKey, String.class);
        if(cacheCode == null){
            // 验证码已失效
            throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
        }
        if(!cacheCode.equals(code)){
            // 验证码错误
            throw new ServiceException(ResultCode.FAILED_ERROR_CODE);
        }
        redisService.deleteObject(emailCodeKey);
    }
    
    private String getEmailCodeKey(String email){
        return CacheConstants.EMAIL_CODE_KEY + email;
    }
    
    private String getCodeTimeKey(String email) {
        return CacheConstants.CODE_TIME_KEY + email;
    }

    public static String generateUsername() {
        String prefix = "user_";  // 用户名前缀
        Random random = new Random();
        // 生成6位随机数字作为用户名后缀
        int randomNum = random.nextInt(900000) + 100000;  // 确保是6位数
        return prefix + randomNum;
    }
}
